<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>浪漫粒子表白动画</title>
  <style>
    body {
      margin: 0;
      overflow: hidden;
      background: #000;
    }

    canvas {
      display: block;
      position: fixed;
      top: 0;
      left: 0;
    }
  </style>
</head>

<body>
  <canvas id="canvas"></canvas>

  <script>
    // 配置参数
    const CONFIG = {
      TEXT: ['我爱你', '有你真好', '一生一世', '你是我的唯一'], // 要显示的文字
      FONT: '微软雅黑', // 字体
      PARTICLE_COLOR: { // 粒子颜色设置
        R: 255,  // 红色
        G: 50,   // 少量绿色
        B: 100   // 少量蓝色
      },
      PARTICLE_RADIUS: 3, // 粒子半径
      PARTICLE_SPEED: 8,  // 粒子移动速度(值越小越快)
      PULSATION: true,    // 是否启用脉动效果
      PULSATION_SPEED: 600, // 脉动速度
      CHANGE_TEXT_INTERVAL: 3000 // 文字切换间隔(毫秒)
    };

    // 初始化变量
    let canvas, ctx;
    let particles = [];
    let currentTextIndex = 0;
    let textPositions = [];
    let canvasWidth, canvasHeight;

    // 初始化函数
    function init() {
      canvas = document.getElementById('canvas');
      ctx = canvas.getContext('2d');

      // 设置画布大小为窗口大小
      resizeCanvas();

      // 初始化粒子
      createParticles();

      // 开始动画
      animate();

      // 设置定时切换文字
      setInterval(changeText, CONFIG.CHANGE_TEXT_INTERVAL);

      // 窗口大小改变时重新调整
      window.addEventListener('resize', resizeCanvas);
    }

    // 调整画布大小
    function resizeCanvas() {
      canvasWidth = canvas.width = window.innerWidth;
      canvasHeight = canvas.height = window.innerHeight;

      // 重新计算文字位置
      calculateTextPositions();
    }

    // 创建粒子
    function createParticles() {
      particles = [];

      // 根据屏幕大小计算粒子数量
      const particleCount = Math.floor((canvasWidth * canvasHeight) / 400);

      for (let i = 0; i < particleCount; i++) {
        particles.push({
          // 初始位置在屏幕四周边缘
          x: Math.random() < 0.5 ?
            (Math.random() < 0.5 ? 0 : canvasWidth) :
            Math.random() * canvasWidth,
          y: Math.random() < 0.5 ?
            (Math.random() < 0.5 ? 0 : canvasHeight) :
            Math.random() * canvasHeight,

          // 目标位置(初始为当前位置)
          tx: 0,
          ty: 0,

          // 粒子颜色(主色调为红色，有些随机变化)
          r: CONFIG.PARTICLE_COLOR.R + Math.round((Math.random() * 255) | 0),
          g: CONFIG.PARTICLE_COLOR.G + Math.round((Math.random() * 255) | 0),
          b: CONFIG.PARTICLE_COLOR.B + Math.round((Math.random() * 255) | 0),

          // 粒子大小
          radius: Math.random() * CONFIG.PARTICLE_RADIUS + 1,

          // 脉动效果相关
          pulseOffset: Math.random() * CONFIG.PULSATION_SPEED
        });
      }
    }

    // 计算文字位置
    function calculateTextPositions() {
      const tempCanvas = document.createElement('canvas');
      const tempCtx = tempCanvas.getContext('2d');
      tempCanvas.width = canvasWidth;
      tempCanvas.height = canvasHeight;

      // 清空临时画布
      tempCtx.fillStyle = 'black';
      tempCtx.fillRect(0, 0, canvasWidth, canvasHeight);

      // 设置文字样式
      const fontSize = Math.min(canvasWidth, canvasHeight) * 0.15;
      tempCtx.font = `bold ${fontSize}px ${CONFIG.FONT}`;
      tempCtx.textAlign = 'center';
      tempCtx.textBaseline = 'middle';
      tempCtx.fillStyle = 'white';

      // 绘制当前文字
      const text = CONFIG.TEXT[currentTextIndex];
      tempCtx.fillText(text, canvasWidth / 2, canvasHeight / 2);

      // 获取像素数据
      const imageData = tempCtx.getImageData(0, 0, canvasWidth, canvasHeight);
      const data = imageData.data;

      // 提取文字位置
      textPositions = [];
      for (let y = 0; y < canvasHeight; y += 4) {
        for (let x = 0; x < canvasWidth; x += 4) {
          const index = (y * canvasWidth + x) * 4;
          if (data[index] > 0) {
            textPositions.push({ x, y });
          }
        }
      }

      // 为粒子分配目标位置
      assignTargetPositions();
    }

    // 为粒子分配目标位置
    function assignTargetPositions() {
      // 复制位置数组以便修改
      const availablePositions = [...textPositions];

      particles.forEach(particle => {
        if (availablePositions.length > 0) {
          // 随机选择一个位置
          const randomIndex = Math.floor(Math.random() * availablePositions.length);
          const position = availablePositions[randomIndex];

          // 分配给粒子
          particle.tx = position.x;
          particle.ty = position.y;

          // 移除已分配的位置
          availablePositions.splice(randomIndex, 1);
        } else {
          // 如果没有位置了，随机放在文字附近
          particle.tx = canvasWidth / 200  ;
          particle.ty = canvasHeight / 200 ;
        }
      });
    }

    // 切换文字
    function changeText() {
      currentTextIndex = (currentTextIndex + 1) % CONFIG.TEXT.length;
      calculateTextPositions();
    }

    // 动画循环
    function animate() {
      // 清空画布
      ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
      ctx.fillRect(0, 0, canvasWidth, canvasHeight);

      // 更新并绘制每个粒子
      particles.forEach(particle => {
        // 移动粒子向目标位置
        particle.x += (particle.tx - particle.x) / CONFIG.PARTICLE_SPEED;
        particle.y += (particle.ty - particle.y) / CONFIG.PARTICLE_SPEED;

        // 脉动效果
        let radius = particle.radius;
        if (CONFIG.PULSATION) {
          const pulse = Math.sin((Date.now() + particle.pulseOffset) / CONFIG.PULSATION_SPEED * Math.PI * 2);
          radius = particle.radius * (0.8 + pulse * 0.2);
        }

        // 绘制粒子
        ctx.beginPath();
        ctx.arc(particle.x, particle.y, radius, 0, Math.PI * 2);
        ctx.fillStyle = `rgb(${particle.r}, ${particle.g}, ${particle.b})`;
        ctx.fill();
      });

      requestAnimationFrame(animate);
    }

    // 启动动画
    window.onload = init;
  </script>
</body>

</html>